package com.javaxiaobear.base.common.utils;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.fasterxml.jackson.databind.type.TypeFactory;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.*;

/**
 * sth
 *
 * @author LarryYan
 * @date 2021/9/24 11:43
 */
public class JsonUtil {

  private static ObjectMapper mapper;
  private static TypeFactory type;

  public static void init(ObjectMapper objectMapper) {
    mapper = objectMapper;
    type = objectMapper.getTypeFactory();
  }

  /** obj转JSON，如果obj==null，则返回null */
  public static String write(Object obj) {
    if (obj == null) {
      return null;
    }
    if (obj instanceof String) {
      return obj.toString();
    }
    try {
      return mapper.writeValueAsString(obj);
    } catch (JsonProcessingException e) {
      throw new IllegalArgumentException("不能序列化成JSON, object : " + obj, e);
    }
  }

  public static <T> T read(String source, Class<T> clazz) {
    if (StrUtil.isEmpty(source)) {
      return null;
    }
    try {
      return mapper.readValue(source, clazz);
    } catch (IOException e) {
      throw new IllegalArgumentException("无法反序列化成" + clazz + ",json : " + source, e);
    }
  }

  /** json转对象，如果json是empty则返回null */
  public static <T> T read(String source, JavaType javaType) {
    if (StrUtil.isEmpty(source)) {
      return null;
    }
    try {
      return mapper.readValue(source, javaType);
    } catch (IOException e) {
      throw new IllegalArgumentException("无法反序列化成" + javaType + ",json : " + source, e);
    }
  }

  /** 读取json string为Map对象 */
  public static <K, V> Map<K, V> readMap(String source, Class<K> keyClass, Class<V> valueClass) {
    return read(source, mapType(HashMap.class, keyClass, valueClass));
  }

  public static <K, V> Map<K, V> readMap(String source, Class<K> keyClass, JavaType valueClass) {
    return read(source, mapType(HashMap.class, keyClass, valueClass));
  }

  /** 读取json string为Map对象 */
  public static Map<String, Object> readMap(
      String source, JavaType keyJavaType, JavaType valueJavaType) {
    return read(source, mapType(keyJavaType, valueJavaType));
  }

  /** 读取json string为Map对象 */
  public static Map<Object, Object> readMap(String source) {
    return readMap(source, Object.class, Object.class);
  }

  /** 读取json string为Map对象 */
  public static Map<String, Object> readMapStr(String source) {
    return readMap(source, String.class, Object.class);
  }

  /** 读取json string为Map对象 */
  public static <T> Map<String, T> readMapStr(String source, Class<T> valueClass) {
    return readMap(source, String.class, valueClass);
  }

  /** 读取json string为Map对象 */
  public static <T> Map<String, T> readMapStr(String source, JavaType valueType) {
    return readMap(source, String.class, valueType);
  }

  /** 读取json string为数组对象 */
  public static <T> T[] readArray(String source, Class<T> clazz) {
    return read(source, arrayType(clazz));
  }

  /** 读取json string为数组对象 */
  public static <T> T[] readArray(String source, JavaType javaType) {
    return read(source, arrayType(javaType));
  }

  /** 读取json string为数组对象 */
  public static <T> T[] readArray(String source) {
    return read(source, arrayType(Object.class));
  }

  public static <T> List<T> readList(String source, Class<T> clazz) {
    return read(source, listType(clazz));
  }

  public static <T> List<T> readList(String source, JavaType javaType) {
    return read(source, listType(javaType));
  }

  public static <T> Set<T> readSet(String source, Class<T> clazz) {
    return read(source, setType(clazz));
  }

  public static <T> Set<T> readSet(String source, JavaType javaType) {
    return read(source, setType(javaType));
  }

  public static <T> Collection<T> readCollection(String source, Class<T> clazz) {
    return read(source, collectionType(clazz));
  }

  /** json转对象，如果是empty则返回null，如果是数组则返回List，如果是对象则返回Map */
  public static Object read(String source) {
    if (StrUtil.isEmpty(source)) {
      return null;
    }

    if (isObject(source)) {
      return read(source, type.constructMapType(HashMap.class, String.class, Object.class));
    } else if (isArray(source)) {
      return read(source, type.constructCollectionType(List.class, Object.class));
    } else {
      return source;
    }
  }

  /** 判断json是否是对象 */
  public static boolean isObject(String source) {
    return source.startsWith("{");
  }

  /** 判断json是否是数组 */
  public static boolean isArray(String source) {
    return source.startsWith("[");
  }

  /** 构建mapType ?<String,?> */
  public static JavaType mapType(
      Class<? extends Map> mapClass, Class<?> keyClass, Class<?> valueClass) {
    return type.constructMapType(mapClass, keyClass, valueClass);
  }

  public static JavaType mapType(
      Class<? extends Map> mapClass, Class<?> keyClass, JavaType valueClass) {
    return type.constructMapType(mapClass, type(keyClass), valueClass);
  }

  public static JavaType mapType(
      Class<? extends Map> mapClass, JavaType keyClass, JavaType valueClass) {
    return type.constructMapType(mapClass, keyClass, valueClass);
  }

  /** 构建mapType Map<?,?> */
  public static JavaType mapType(Class<?> keyClass, Class<?> valueClass) {
    return mapType(HashMap.class, keyClass, valueClass);
  }

  public static JavaType mapType(Class<?> keyClass, JavaType valueClass) {
    return mapType(HashMap.class, keyClass, valueClass);
  }

  public static JavaType mapType(JavaType keyClass, JavaType valueClass) {
    return mapType(HashMap.class, keyClass, valueClass);
  }

  /** 构建mapType Map<String,?> */
  public static JavaType mapTypeStr(Class<?> valueClass) {
    return mapType(String.class, valueClass);
  }

  /** 构建mapType Map<String,String> */
  public static JavaType mapTypeStr() {
    return mapType(String.class, String.class);
  }

  /** 构建集合JavaType */
  public static JavaType collectionType(
      Class<? extends Collection> collectionClass, Class valueClass) {
    return type.constructCollectionType(collectionClass, valueClass);
  }

  /** 构建集合JavaType */
  public static JavaType collectionType(
      Class<? extends Collection> collectionClass, JavaType valueJavaType) {
    return type.constructCollectionType(collectionClass, valueJavaType);
  }

  /** 构建集合JavaType Collection<?> */
  public static JavaType collectionType(Class valueClass) {
    return collectionType(Collection.class, valueClass);
  }

  /** 构建list JavaType List<?> */
  public static JavaType listType(Class valueClass) {
    return collectionType(List.class, valueClass);
  }

  /** 构建list JavaType List<?> */
  public static JavaType listType(JavaType valueJavaType) {
    return collectionType(List.class, valueJavaType);
  }

  /** 构建set JavaType Set<?> */
  public static JavaType setType(Class valueClass) {
    return collectionType(Set.class, valueClass);
  }

  /** 构建set JavaType Set<?> */
  public static JavaType setType(JavaType javaType) {
    return collectionType(Set.class, javaType);
  }

  /** 构建数组 JavaType */
  public static JavaType arrayType(Class valueClass) {
    return type.constructArrayType(valueClass);
  }

  /** 构建数组 JavaType */
  public static JavaType arrayType(JavaType javaType) {
    return type.constructArrayType(javaType);
  }

  public static JavaType parametricType(Class<?> parametrized, Class... parameterClasses) {
    return type.constructParametricType(parametrized, parameterClasses);
  }

  public static JavaType parametricType(Class<?> parametrized, JavaType... javaTypes) {
    return type.constructParametricType(parametrized, javaTypes);
  }

  public static <T> T convert(Object obj, JavaType javaType) {
    return read(write(obj), javaType);
  }

  public static <T> T convert(Object obj, Class<T> clazz) {
    return read(write(obj), clazz);
  }

  public static JavaType type(Type t) {
    return type.constructType(t);
  }

  public static JavaType type(TypeReference<?> typeRef) {
    return type.constructType(typeRef);
  }

  public static ObjectWriter writer(SimpleFilterProvider filterProvider) {
    return mapper.writer(filterProvider);
  }

  public static ObjectMapper getMapper() {
    return mapper;
  }
}
